package club.kingyin.easycache.component.annotation.handler;

import club.kingyin.easycache.component.ExceptionCacheHandler;
import club.kingyin.easycache.component.ExceptionFilter;
import club.kingyin.easycache.component.ResultConverter;
import club.kingyin.easycache.component.annotation.EasyCache;
import club.kingyin.easycache.exception.InvokeException;
import club.kingyin.easycache.method.CacheMethod;
import club.kingyin.easycache.utils.InstanceUtils;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class AnnotationInvokeExceptionHandler extends ExceptionCacheHandler {

    private static Map<Class<? extends ExceptionFilter>, ExceptionFilter> filterMap;

    private static Map<Class<? extends ResultConverter>, ResultConverter> converterMap;

    @Override
    protected boolean filter(CacheMethod invocation, InvokeException e) {
        if (invocation.getMethod().isAnnotationPresent(club.kingyin.easycache.component.annotation.InvokeException.class)) {
            club.kingyin.easycache.component.annotation.InvokeException invokeException = getInvokeExceptionByMethod(invocation.getMethod());
            Boolean filter = doFilter(invocation, e, invokeException);
            if (filter != null) return filter;
        } else if (invocation.getMethod().isAnnotationPresent(EasyCache.class)) {
            EasyCache easyCache = invocation.getMethod().getDeclaredAnnotation(EasyCache.class);
            if (easyCache.exception().length > 0) {
                club.kingyin.easycache.component.annotation.InvokeException invokeException = easyCache.exception()[0];
                Boolean filter = doFilter(invocation, e, invokeException);
                if (filter != null) return filter;
            }
        }
        return false;
    }

    private Boolean doFilter(CacheMethod invocation, InvokeException e, club.kingyin.easycache.component.annotation.InvokeException invokeException) {
        try {
            init();
            ExceptionFilter filter = filterMap.getOrDefault(invokeException.filter(), (ExceptionFilter) InstanceUtils.newInstance(invokeException.filter()));
            log.debug("方法：[{}] 装载异常处理器 {}", invocation.getMethod().getName(), filter);
            return filter.filter(e);
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
                 InvocationTargetException instantiationException) {
            instantiationException.printStackTrace();
        }
        return null;
    }

    club.kingyin.easycache.component.annotation.InvokeException getInvokeExceptionByMethod(Method method) {
        return method.getDeclaredAnnotation(club.kingyin.easycache.component.annotation.InvokeException.class);
    }

    @Override
    protected Object unusualResultConversion(CacheMethod invocation, InvokeException e) {
        if (invocation.getMethod().isAnnotationPresent(club.kingyin.easycache.component.annotation.InvokeException.class)) {
            club.kingyin.easycache.component.annotation.InvokeException invokeException = getInvokeExceptionByMethod(invocation.getMethod());
            return conversion(invokeException, e);
        } else if (invocation.getMethod().isAnnotationPresent(EasyCache.class)) {
            EasyCache easyCache = invocation.getMethod().getDeclaredAnnotation(EasyCache.class);
            if (easyCache.exception().length > 0) {
                club.kingyin.easycache.component.annotation.InvokeException invokeException = easyCache.exception()[0];
                return conversion(invokeException, e);
            }
        }
        return e;
    }

    void init() {
        if (converterMap == null) {
            converterMap = new ConcurrentHashMap<>();
        }
        if (filterMap == null) {
            filterMap = new HashMap<>();
        }
    }

    public Object conversion(club.kingyin.easycache.component.annotation.InvokeException invokeException, InvokeException e) {
        try {
            init();
            ResultConverter converter = converterMap.getOrDefault(invokeException.result(), (ResultConverter) InstanceUtils.newInstance(invokeException.result()));
            Object converterRes = converter.conversion(e);
            log.warn("异常转换：{}->{}", e.getMessage(), converterRes);
            return converterRes;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException instantiationException) {
            instantiationException.printStackTrace();
        }
        return e;
    }
}
